package com.helint.component.encryption.filter;

import cn.hutool.json.JSONUtil;
import com.helint.component.encryption.prop.RsaProperties;
import com.helint.component.encryption.util.AESUtil;
import com.helint.component.encryption.wrapper.RequestWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.multipart.MultipartResolver;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;


/**
 * 加密解密过滤器
 *
 * @author junjunhuang
 * @version @version 1.0.0
 * @date 2024/01/25 10:55
 */
//@Order(12)
@Slf4j
@Component
// 指定对所有请求都进行拦截，注意需要配合：@ServletComponentScan(basePackages="xx") 一起使用才生效
//@WebFilter(urlPatterns = "/*")
@AllArgsConstructor
public class EnDecryptionFilter implements Filter {
    // 需要特殊处理的请求头
    private static final String AKS_HEADER = "aksEncrypt";
    // 加密参数名称
    private static final String AKS_PARAMETER = "encryptData";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_POST = "POST";
    // 是否为文件上传请求
    private static final String MULTIPART_FORM ="multipart/form-data";

    private final RsaProperties properties;
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        // 获取并转换请求对象
        HttpServletRequest request = this.getHttpServletRequest(servletRequest);
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String url = request.getRequestURL().toString();
        // 如果不需要处理，直接返回
        if(!properties.getEnable()){
            log.debug("请求：{},非加密请求，不解密！", url);
            chain.doFilter(request, response);
            return;
        }

        // 判断白名单
        if (checkUrls(properties.getIgnoreUrls(), request.getRequestURI())) {
            chain.doFilter(request, response);
            return;
        }


        // 包装请求对象
        RequestWrapper requestWrapper = new RequestWrapper(request);
        // POST 请求
        if(StringUtils.equalsIgnoreCase(requestWrapper.getMethod(), METHOD_POST)) {
            // 获取加密之前的请求数据
            String encryptData = requestWrapper.getBody();
            requestWrapper.setAttribute(AKS_PARAMETER, encryptData);// 这行主要是 解密时候 需要传入加密原始参数，所以保存了下
            log.info("Post请求:{}，待解密请求参数：{}", url, encryptData);
            // 解密数据，这里调用Service进行解密
            String decryptoData = AESUtil.decryptCBC(encryptData,properties.getAesKey() );
            log.info("Post请求:{}，解密之后参数：{}", url, decryptoData);
            // 特别声明，Post请求参数应该是JSON格式加密后的数据
            requestWrapper.setBody(decryptoData);
        }
        // Get 请求
        else if(StringUtils.equalsIgnoreCase(requestWrapper.getMethod(), METHOD_GET)){
            String encryptData = request.getParameter(AKS_PARAMETER);
            log.info("Get请求:{}，待解密请求参数：{}", url, encryptData);
            if(StringUtils.isNotBlank(encryptData)){
                requestWrapper.setAttribute(AKS_PARAMETER, encryptData);// 这行主要是 解密时候 需要传入加密原始参数，所以保存了下
                // 解密数据
                String decryptoData = AESUtil.decryptCBC(encryptData,properties.getAesKey());
                log.info("Get请求:{}，解密之后参数：{}", url, decryptoData);
                requestWrapper.setParameterMap(JSONUtil.parseObj(decryptoData));
            }
        }
        chain.doFilter(requestWrapper, response);
    }

    public static void main(String[] args) {
        System.out.println(AESUtil.encryptCBC("{\"userId\":\"123456\"}", "G99YPUMGNO78FG8W04LKXKUHMO89AUQ0"));
    }

    /**
     * 转换为HttpServletRequest 对象
     * @param servletRequest 请求对象
     * @return  HttpServletRequest
     */
    private HttpServletRequest getHttpServletRequest(ServletRequest servletRequest) {
        String enctype = servletRequest.getContentType();
        if (StringUtils.isNotBlank(enctype) && enctype.contains(MULTIPART_FORM)) {
            // 注意：MultipartResolver springboot 已经默认注入了，如果没有的话，需要手动注入
            // 如果不使用MultipartResolver那么request.getParameter(name)，就获取不到参数，这里spring帮我们做了参数解析
            return this.getBean(servletRequest.getServletContext(), MultipartResolver.class).resolveMultipart((HttpServletRequest) servletRequest);
        }
        return (HttpServletRequest) servletRequest;
    }


    /**
     * 获取Spring中定义的Bean都对象
     * @param sc  ServletContext
     * @param requiredType 需要的类型
     * @param <T> 结果
     * @return 结果
     */
    private <T> T getBean(ServletContext sc, Class<T> requiredType){
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sc);
        return  Objects.requireNonNull(ctx).getBean(requiredType);
    }

    /**
     * 对url进行校验匹配
     */
    private boolean checkUrls(List<String> urls, String path) {
        return urls.stream().anyMatch(url -> pathMatcher.match(url, path));
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

